package com.jiashihui.web.service.impl;

import com.jiashihui.web.mapper.UserMapper;
import com.jiashihui.web.model.test.User;
import com.jiashihui.web.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @author jiashihui
 * @describe: 缓存
 * @date 2019/01/09
 *
 * @Cacheable
 * 可以标记在一个方法上，也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的，当标记在一个类上时则表示该类所有的方法都是支持缓存的。
 * 使用@Cacheable标注的方法，Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素，如果存在就不再执行该方法，而是直接从缓存中获取结果进行返回，否则才会执行并将返回结果存入指定的缓存中
 * 对于一个支持缓存的方法，Spring会在其被调用后将其返回值缓存起来，以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果，而不需要再次执行该方法。
 * Spring在缓存方法的返回值时是以键值对进行缓存的，值就是方法的返回结果，至于键的话，Spring又支持两种策略，默认策略和自定义策略。
 * 需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable可以指定三个属性，value、key和condition。
 * value属性是必须指定的，其表示当前方法的返回值是会被缓存在哪个Cache上的，对应Cache的名称。其可以是一个Cache也可以是多个Cache，当需要指定多个Cache时其是一个数组。
 *  例子：@Cacheable({"cache1", "cache2"})//Cache是发生在cache1和cache2上的
 * @CachePut
 * 也可以声明一个方法支持缓存功能。
 * 与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果，而是每次都会执行该方法，并将执行结果以键值对的形式存入指定的缓存中。
 * @CacheEvict
 * 用来标注在需要清除缓存元素的方法或类上的。
 * allEntries属性:allEntries是boolean类型，表示是否需要清除缓存中的所有元素。默认为false，表示不需要。当指定了allEntries为true时，Spring Cache将忽略指定的key。
 *  例子：@CacheEvict(value="users", allEntries=true)
 * beforeInvocation属性：清除操作默认是在对应方法成功执行之后触发的，即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。
 *                        使用beforeInvocation可以改变触发清除操作的时间，当我们指定该属性值为true时，Spring会在调用该方法之前清除缓存中的指定元素。
 *  例子：@CacheEvict(value="users", beforeInvocation=true)
 * @Caching
 * Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性：cacheable、put和evict，分别用于指定@Cacheable、@CachePut和@CacheEvict。
 *  例子：@Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),@CacheEvict(value = "cache3", allEntries = true) })
 * @CacheConfig
 * 所有的@Cacheable（）里面都有一个value＝“xxx”的属性，这显然如果方法多了，写起来也是挺累的，如果可以一次性声明完 那就省事了， 所以，有了@CacheConfig这个配置。
 * 如果你在你的方法写别的名字，那么依然以方法的名字为准。
 * @使用自定义注解：@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Cacheable(value="users")public @interface MyCacheable
 *                  那么在我们需要缓存的方法上使用@MyCacheable进行标注也可以达到同样的效果。@MyCacheable
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper mapper;

    /**
     * @Cacheable 应用到读取数据的方法上，先从缓存中读取，如果没有再从DB获取数据，然后把数据添加到缓存中
     * unless 表示条件表达式成立的话不放入缓存
     * @param userId
     * @return
     */
    @Cacheable(value = "user", key = "#userId", unless = "#result eq null")
    @Override
    public User getUserById(Integer userId) {
        return mapper.selectByPrimaryKey(userId);
    }

    /**
     * @CachePut 应用到写数据的方法上，如新增/修改方法，调用方法时会自动把相应的数据放入缓存
     * @param user
     * @return
     */
    @CachePut(value = "user", key = "#user.userId", condition = "#result ne null")
    @Override
    public User saveUser(User user) {
        return mapper.insertSelective(user) > 0 ? user : null;
    }

    /**
     * @CacheEvict 应用到删除数据的方法上，调用方法时会从缓存中删除对应key的数据
     * @param userId
     * @return
     */
    @CacheEvict(value = "user", key = "#userId", condition = "#result eq true")
    @Override
    public boolean removeUserById(Integer userId) {
        return mapper.deleteByPrimaryKey(userId) > 0;
    }
}
